//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// File writer utility.
//

# include "StdAfx.h"
# include "FileWriter.h"
# include "Log.h"
# include "fast_floor.h"
# include "Wrappers.h"

const size_t c_cBufferSize = 65536;

CFileWriter::CFileWriter (HANDLE _hFile) :
	m_hFile (_hFile)
{
	m_pBuffer = new char [c_cBufferSize];
	m_cBufferPosition = 0;

	m_bOverflow = false;
	m_cPos = 0;
	m_cLine = 1;
}

void CFileWriter::SetPos (size_t _cPos) {
	FlushBuffer ();

	m_cPos = _cPos;
	m_cBufferPosition = 0;
	::SetFilePointer (m_hFile, _cPos, NULL, FILE_BEGIN);
}

void CFileWriter::FlushBuffer () {
	if (m_cBufferPosition == 0)
		return;

	DWORD dwWritten = 0;
	::WriteFile (m_hFile, m_pBuffer, m_cBufferPosition, & dwWritten, NULL);
	if (dwWritten < m_cBufferPosition)
		m_bOverflow = true;
	if (m_bOverflow)
		ReportWarning ("Write error: 0x%x.", ::GetLastError ());
		
	m_cBufferPosition = 0;
}

void CFileWriter::WriteBin (size_t _cLen, BYTE _btFill) {
	BYTE * const pBytes = _cLen < 32768 ? reinterpret_cast<BYTE *> (alloca (_cLen)) : new BYTE [_cLen];
	CByteArrayHolder bah (_cLen >= 32768 ? pBytes : NULL);
	::memset (pBytes, _btFill, _cLen);
	WriteBin (pBytes, _cLen);
}

void CFileWriter::WriteBin (const void * _pData, size_t _cLen) {
	m_cPos += _cLen;

	if (_cLen > c_cBufferSize) {
		FlushBuffer ();

		DWORD dwWritten = 0;
		::WriteFile (m_hFile, _pData, _cLen, & dwWritten, NULL);
		if (dwWritten < _cLen)
			m_bOverflow = true;

		return;
	}

	if (m_cBufferPosition + _cLen > c_cBufferSize)
		FlushBuffer ();

	assert (m_cBufferPosition + _cLen <= c_cBufferSize);
	::memcpy (m_pBuffer + m_cBufferPosition, _pData, _cLen);
	m_cBufferPosition += _cLen;
}

void CFileWriter::WriteS (const char * _str, size_t _cLen) {
	m_cPos += _cLen;

	for (const char * p = _str; * p; ++ p)
		if (* p == '\n')
			++ m_cLine;

	if (_cLen > c_cBufferSize) {
		FlushBuffer ();

		DWORD dwWritten = 0;
		::WriteFile (m_hFile, _str, _cLen, & dwWritten, NULL);
		if (dwWritten < _cLen)
			m_bOverflow = true;

		return;
	}

	if (m_cBufferPosition + _cLen > c_cBufferSize)
		FlushBuffer ();

	assert (m_cBufferPosition + _cLen <= c_cBufferSize);
	::memcpy (m_pBuffer + m_cBufferPosition, _str, _cLen);
	m_cBufferPosition += _cLen;
}

void __cdecl CFileWriter::WriteFmt (const char * _strFormat, ...) {
	va_list vl;
	va_start (vl, _strFormat);

	const size_t BUF_LEN = 512;
	char buf [BUF_LEN + 1];
	const size_t cLen = ::_vsnprintf (buf, BUF_LEN, _strFormat, vl);

	assert (cLen <= BUF_LEN);
	if (cLen <= BUF_LEN)
		WriteS (buf, cLen);

	va_end (vl);
}

void CFileWriter::WriteU (unsigned _u) {
	char buf [16];
	buf [15] = '\0';
	for (size_t c = 14; c > 0; -- c) {
		buf [c] = '0' + (_u % 10);
		_u /= 10;
		if (_u == 0) {
			WriteS (buf + c);
			return;
		}
	}
	assert (false);
}

void CFileWriter::WriteX (unsigned _x) {
	char buf [10];
	::ultoa (_x, buf, 16);
	WriteS (buf);
}

void CFileWriter::WriteF (float _f) {
	if (_f < 0) {
		WriteS ("-");
		_f = -_f;
	}

	_f += .5f/m_fPrecFactor;

	const unsigned uInt = fast_floor (_f);

	WriteU (uInt);
	WriteC ('.');
	_WriteFraction (_f - uInt);
}

void CFileWriter::_WriteFraction (float _f) {
	assert (_f >= 0);
	unsigned u = fast_floor (_f*m_fPrecFactor);

	char * const buf = (char *) ::alloca ((m_cPrecOrder + 2)*sizeof (char));
	for (size_t c = 0; c < m_cPrecOrder; ++ c) {
		buf [m_cPrecOrder - c - 1] = '0' + (u % 10);
		u /= 10;
	}
	buf [m_cPrecOrder] = '\0';

	WriteS (buf);
}

void CFileWriter::WriteDF (double _f) {
	if (_f < 0) {
		WriteS ("-");
		_f = -_f;
	}

	_f += .5/m_fPrecFactor;

	const unsigned uInt = fast_floor_d (_f);

	WriteU (uInt);
	WriteC ('.');
	_WriteFractionD (_f - uInt);
}

void CFileWriter::_WriteFractionD (double _f) {
	assert (_f >= 0);
	unsigned u = fast_floor_d (_f*m_fPrecFactor);

	char * const buf = (char *) ::alloca ((m_cPrecOrder + 2)*sizeof (char));
	for (size_t c = 0; c < m_cPrecOrder; ++ c) {
		buf [m_cPrecOrder - c - 1] = '0' + (u % 10);
		u /= 10;
	}
	buf [m_cPrecOrder] = '\0';

	WriteS (buf);
}

void CFileWriter::SetFPrecision (size_t _cPrecOrder) {
	m_cPrecOrder = _cPrecOrder;
	m_fPrecFactor = ::pow (10.f, m_cPrecOrder);
}
